/*
 *	 			chenyu <chenyu@hpclab.cs.tsinghua.edu.cn>
 * 01/21/2003: ne2k driver for lwip(OS independent) initial version
 * 				yangye <yangye@163.net>
 */


#include "lwip/debug.h"

#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/ip.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include "arch/cc.h"

#include "netif/arp.h"
#include "netif/ne2kif.h"

#define outb(v,a) ((*(volatile unsigned char *)(a)) = (v))
#define inb(a) (*(volatile unsigned char *)(a))

struct RTL8019if {
  struct eth_addr *ethaddr;
  /* Add whatever per-interface state that is needed here. */
};

const struct eth_addr ethbroadcast = {0xff,0xff,0xff,0xff,0xff,0xff};

struct netif *rtl8019if_netif;   //points to the real netif ,used by ne2k_isr

static void ne2k_copyin(u16_t count, u8_t *buf);
static void ne2k_copyout(u16_t count, u8_t *buf);
static void ne2k_discard(u16_t count);
void ne2k_isr(void);
static void low_level_init(struct netif * netif);
static struct pbuf * low_level_receive(struct RTL8019if *rtl8019if);
static err_t low_level_send(struct RTL8019if *rtl8019if,struct pbuf *p);



/*
 * Read the specified number of bytes from the device DMA port into
 * the supplied buffer.
 */
static void ne2k_copyin(u16_t count, u8_t *buf)
{
  while(count--) {
    *buf++  = inb(NE_DMA);
  }
}

/*
 * Write the specified number of bytes from the device DMA port into
 * the supplied buffer.
 */ 
static void ne2k_copyout(u16_t count, u8_t *buf)
{
	while(count--) {
		outb(*buf++,NE_DMA);
	}
}

/*
 * Pull the specified number of bytes from the device DMA port,
 * and throw them away.
 */
static void ne2k_discard(u16_t count)
{
	u8_t tmp;
	while(count--) {
		tmp = inb(NE_DMA);
	}
}


// void NICISR(void) interrupt 
void ne2k_isr(void)
{
	u8_t  isr,curr,bnry;
	struct netif *netif;
	
	//close nic
	outb(CMD_PAGE0 | CMD_NODMA | CMD_STOP,NE_CR);
	
	//in PAGE0
	isr = inb(NE_ISR);

	// ram overflow interrupt
	if (isr & ISR_OVW) {
		outb(ISR_OVW,NE_ISR);		// clear interrupt
	//	ne2k_overflowProcess();              //yangye :no overflow now 
	}
	
	// error transfer interrupt ,NIC abort tx due to excessive collisions	
	if (isr & ISR_TXE) {
		outb(ISR_TXE,NE_ISR);		// clear interrupt
	 	//temporarily do nothing
	}

	// Rx error , reset BNRY pointer to CURR (use SEND PACKET mode)
	if (isr & ISR_RXE) {
		outb(ISR_RXE,NE_ISR);		// clear interrupt
		
		outb(CMD_PAGE1 | CMD_NODMA | CMD_STOP,NE_CR);
		curr = inb(NE_CURR);
		outb(CMD_PAGE0 | CMD_NODMA | CMD_STOP,NE_CR);
		outb(curr, NE_BNRY);
	}
	
	//got packet with no errors
	if (isr & ISR_PRX) {
		outb(ISR_PRX, NE_ISR);		// clear interrupt

		outb(CMD_PAGE1 | CMD_NODMA | CMD_STOP, NE_CR);
		curr  =  inb(NE_CURR);
		outb(CMD_PAGE0 | CMD_NODMA | CMD_STOP, NE_CR);
		bnry = inb(NE_BNRY);
		//yangye 2003-1-21
		//get more than one packet until receive buffer is empty
		while(curr != bnry){
			ne2k_recv_packet(rtl8019if_netif);
			outb(CMD_PAGE1 | CMD_NODMA | CMD_STOP, NE_CR);
			curr =  inb(NE_CURR);
			outb(CMD_PAGE0 | CMD_NODMA | CMD_STOP, NE_CR);
			bnry = 	inb(NE_BNRY);			
			}
	}
		
	//Transfer complelte, do nothing here
	if( isr & ISR_PTX){
#ifdef LWIP_DEBUG
		PRINT("ne2k_isr: is ISR_PTX\n");
#endif
		outb(ISR_PTX, NE_ISR);          // clear interrupt
	}
		
	outb(CMD_PAGE0 | CMD_NODMA | CMD_STOP, NE_CR);
	outb(0xff, NE_ISR);			// clear ISR	
	
	//open nic for next packet
	outb(CMD_PAGE0 | CMD_NODMA | CMD_RUN, NE_CR);
}



/**
 * Initialize the rtk8019as, resetting the interface and getting the ethernet
 * address.
 */
static void 
low_level_init(struct netif * netif)
{
	u8_t i;
	struct RTL8019if *rtl8019if;
	u8_t mac_addr[6];
	
#if 0
	u8_t value;
	unsigned long addr;

	for(addr = NE_BASE; addr <= (NE_BASE+NET_OFFSET); addr++) {
		value = inb(addr);
		uart_printf("%d ",value);
	}
#endif

	//i = inb(S3C44B0_NET_BASE);
	//uart_printf("%d\n",i);

	outb(0x22, NE_CR);
	rtl8019if = netif->state;
	
	//yangye 2003-1-21
	//get mac addr from ne2k PROM
	//ft! sim_ne2k not support dma read now!
	//hack : manual write mac addr
	//chy 2003-04-27: delete below code
	/*
	{
	   mac_addr[0] = 0;
	   mac_addr[1] = 1;
	   mac_addr[2] = 2;
	   mac_addr[3] = 3;
	   mac_addr[4] = 4;
	   mac_addr[5] = 5;
	}
	*/
	
	//to do: add dam read for sim_ne2k
	

/*
 * Initialize physical device
 */
	//write and read 0x1f to reset the nic
	outb(0, NE_RESET);
	inb(NE_RESET);            
	for(i=0; i < 20; i++)		//delay at least 10ms
		;

	//in PAGE0
	outb(CMD_PAGE0 | CMD_NODMA | CMD_STOP, NE_CR);
	/* FIFO threshold = 8 bytes, normal operation, 8-bit byte write DMA, BOS=0 */
	outb(DCR_LS | DCR_FIFO8, NE_DCR);
	
	outb(0, NE_RBCR0);
	outb(0, NE_RBCR1);
	
#if	0   //yangye: don't use in skyeye , maybe later
/*
 * Promicuous receive(receive all the packets), including bad packets.
 */
	outb(RCR_AB | RCR_AM | RCR_SEP | RCR_PRO, NE_RCR);
#else
/*
 * Allow broadcast packets, in addition to packets unicast to us.
 * Multicast packets that match MAR filter bits will also be
 * allowed in.
 */
	outb(RCR_AB, NE_RCR);
#endif

	//Place the SNIC in LOOPBACK mode 1 or 2 (Transmit Configuration Register e 02H or 04H)
	outb(TCR_LOOP_INT, NE_TCR);

	outb(XMIT_START >> 8, NE_TPSR);
	outb(RECV_START >> 8, NE_PSTART);
	outb(RECV_START >> 8, NE_BNRY);
	outb(RECV_STOP >> 8, NE_PSTOP);
	//in PAGE1
	outb(CMD_PAGE1 | CMD_NODMA | CMD_STOP, NE_CR);
	outb(RECV_START >> 8, NE_CURR);

/*
 * Set physical address here.(not use 93c46)
	outb(mac_addr[0], NE_PAR0);
	outb(mac_addr[1], NE_PAR1);
	outb(mac_addr[2], NE_PAR2);
	outb(mac_addr[3], NE_PAR3);
	outb(mac_addr[4], NE_PAR4);
	outb(mac_addr[5], NE_PAR5);
 */
        //chy 2003-04-27: get mac addr from 
        mac_addr[0]=inb(NE_PAR0);
        mac_addr[1]=inb(NE_PAR1);
        mac_addr[2]=inb(NE_PAR2);
        mac_addr[3]=inb(NE_PAR3);
        mac_addr[4]=inb(NE_PAR4);
        mac_addr[5]=inb(NE_PAR5);

	#if 1
	uart_printf("%d\t%d\t%d\t%d\t%d\t%d\n",
				mac_addr[0],mac_addr[1],mac_addr[2],
				mac_addr[3],mac_addr[4],mac_addr[5]);
	#endif

  	/* make up an address. */
  	rtl8019if->ethaddr->addr[0] = mac_addr[0];
  	rtl8019if->ethaddr->addr[1] = mac_addr[1];
  	rtl8019if->ethaddr->addr[2] = mac_addr[2];
  	rtl8019if->ethaddr->addr[3] = mac_addr[3];
  	rtl8019if->ethaddr->addr[4] = mac_addr[4];
  	rtl8019if->ethaddr->addr[5] = mac_addr[5];
	
	//Clear multicast filters.(reject all multicast)
	//outb(0, NE_MAR0); outb(0, NE_MAR1);
	//outb(0, NE_MAR2); outb(0, NE_MAR3);
	//outb(0, NE_MAR4); outb(0, NE_MAR5);
	//outb(0, NE_MAR6); outb(0, NE_MAR7);
	
	//select PAGE0 and start the nic
	outb(CMD_PAGE0 | CMD_NODMA | CMD_RUN, NE_CR);
	//set Interrupt mask reg
	outb(ISR_OVW | ISR_TXE | ISR_PTX | ISR_PRX, NE_IMR);
	
	outb(TCR_LOOP_NONE, NE_TCR);
	//clear all interrupts
	outb(0xff, NE_ISR);

 	//enable at91 int 16 for sim_ne2k
	//enbale_net_irq;
	
 	rtl8019if_netif = netif;
}



/*
 * Function to do the actual writing of a packet into the devices
 * transmit buffer.  INT is disable during the function!!!!
 */
static err_t 
low_level_send(struct RTL8019if *rtl8019if,struct pbuf *p)
{
	struct pbuf *q;
	u8_t isr;

	u16_t padLength,packetLength;

/*
 * Set up to transfer the packet contents to the NIC RAM.
 */
	padLength = 0;
	packetLength = p->tot_len;
	 //packetLength muse >=64 (see 802.3)
    if ((p->tot_len) < 64)
   {
       padLength = 64 - (p->tot_len);
       packetLength = 64;
   }
   	
   	//yangye 2003-1-21
	//don't close nic,just close receive interrupt	
	outb(CMD_PAGE2 | CMD_NODMA | CMD_RUN, NE_CR);
	isr = inb(NE_IMR);
	isr &= ~ISR_PRX;
      	outb(CMD_PAGE0 | CMD_NODMA | CMD_RUN, NE_CR);
	outb(isr, NE_IMR);
	
	outb(ISR_RDC, NE_ISR);	
	//yangye 2003-1-21
	//or close nic(CMD_STOP) during receive ???
	//outb(CMD_PAGE0 | CMD_NODMA | CMD_STOP, NE_CR);
	
	
    // Amount to send
	outb(packetLength & 0xff, NE_RBCR0);
	outb(packetLength >> 8, NE_RBCR1);

    // Address on NIC to store
	outb(XMIT_START & 0xff, NE_RSAR0);
	outb(XMIT_START >> 8, NE_RSAR1);
	// Write command to start
	outb(CMD_PAGE0 | CMD_WRITE | CMD_RUN, NE_CR);    

/*
 * Write packet to ring buffers.
 */
   for(q = p; q != NULL; q = q->next) {
    /* Send the data from the pbuf to the interface, one pbuf at a
       time. The size of the data in each pbuf is kept in the ->len
       variable. */
	ne2k_copyout(q->len, q->payload);
    }

	while(padLength-- > 0){	
		outb(0, NE_DMA); 	// Write padding for undersized packets
	}
    
	//  Wait for remote dma to complete - ISR Bit 6 clear if busy
	while((u8_t)(inb(NE_ISR) & ISR_RDC) == 0 );
		
	outb(ISR_RDC, NE_ISR);     //clear RDC
/*
 * Issue the transmit command.(start local dma)
 */
 	outb(XMIT_START >> 8, NE_TPSR);
	outb(packetLength & 0xff, NE_TBCR0);
	outb(packetLength >> 8, NE_TBCR1);	
	//  Start transmission (and shut off remote dma)
	//  and reopen nic(CMD_RUN)
	outb(CMD_PAGE0 | CMD_NODMA | CMD_XMIT | CMD_RUN, NE_CR);
		
	//yangye 2003-1-21
	//reopen receive interrupt
	outb(CMD_PAGE2 | CMD_NODMA | CMD_RUN, NE_CR);
	isr = inb(NE_IMR);
	isr |= ISR_PRX;
     	outb(CMD_PAGE0 | CMD_NODMA | CMD_RUN, NE_CR);
	outb(isr, NE_IMR);
	
	return ERR_OK;
}


/**
 * Read a packet into a pbuf chain.
 */
static struct pbuf * 
low_level_receive(struct RTL8019if *rtl8019if)
{
	u16_t  packetLength,len;
	u8_t PDHeader[18];   // Temp storage for ethernet headers
	struct pbuf * p;
	struct pbuf * q;
	u8_t * payload;
	

	outb(ISR_RDC, NE_ISR);
	outb(0x0f, NE_RBCR1); 	/* See controller manual , use send packet command */
	outb(CMD_PAGE0 | CMD_SEND | CMD_RUN, NE_CR);

	//get the first 18 bytes from nic 
	ne2k_copyin(18,PDHeader);

	//  Store real length, set len to packet length - header
	packetLength = ((unsigned) PDHeader[2] | (PDHeader[3] << 8 ));

   	//verify if the packet is an IP packet or ARP packet
	if((PDHeader[3]>0x06)||(PDHeader[16] != 8)||(PDHeader[17] != 0 && PDHeader[17] != 6))
        {
	     ne2k_discard(packetLength-14);
             return NULL;
        }  
    	
	  /* We allocate a pbuf chain of pbufs from the pool. */

	p = pbuf_alloc(PBUF_LINK, packetLength, PBUF_POOL);
	if(p != NULL) {
		/* We iterate over the pbuf chain until we have read the entire
      		 packet into the pbuf. */
           	// This assumes a minimum pbuf size of 14 ... a good assumption
           	memcpy(p->payload, PDHeader + 4, 14);
	
		for(q = p; q != NULL; q= q->next){
     		   /* Read enough bytes to fill this pbuf in the chain. The
         	  avaliable data in the pbuf is given by the q->len
         	  variable. */
      		  /* read data into(q->payload, q->len); */
     		  // First 14 bytes are already there, skip them
      	
		  payload = q->payload;
		  
		  len = q->len;

		  if (q == p){
           	    payload += 14;
		    len -=14;
           	  }
		ne2k_copyin(len,payload);
      		
		}//for
	} else {
    /* no more PBUF resource, Discard packet in buffer. */
	ne2k_discard(packetLength-14);
  	}
	return p;
}


/*-----------------------------------------------------------------------------------*/
/*
 * ethernetif_init():
 *
 * Should be called at the beginning of the program to set up the
 * network interface. It calls the function low_level_init() to do the
 * actual setup of the hardware.
 *
 *///  WARNING: must close all interrupts during init!!!!
/*-----------------------------------------------------------------------------------*/
void
ne2k_init(struct netif *netif)
{
  struct RTL8019if *rtl8019if;

  rtl8019if = mem_malloc(sizeof(struct RTL8019if));
  netif->state = rtl8019if;
  netif->name[0] = 'e';
  netif->name[1] = 't';
  netif->output = ne2k_send_packet;
  
  low_level_init(netif);
  arp_init();
}


/**
 * Send a packet to the RTK8019as from a series of pbuf buffers.
 */
err_t 
ne2k_send_packet(struct netif *netif, struct pbuf *p,
		  struct ip_addr *ipaddr)
{
  struct RTL8019if *rtl8019if;
  struct pbuf *q;
  struct eth_hdr *ethhdr;
  struct eth_addr *dest, mcastaddr;
  struct ip_addr *queryaddr;
  err_t err;
  u8_t i;
  
  rtl8019if = netif->state;

  /* Make room for Ethernet header. */
  if(pbuf_header(p, 14) != 0) {
    /* The pbuf_header() call shouldn't fail, but we allocate an extra
       pbuf just in case. */
    q = pbuf_alloc(PBUF_LINK, 14, PBUF_RAM);
    if(q == NULL) {
      return ERR_MEM;
    }
    pbuf_chain(q, p);
    p = q;
  }

  /* Construct Ethernet header. Start with looking up deciding which
     MAC address to use as a destination address. Broadcasts and
     multicasts are special, all other addresses are looked up in the
     ARP table. */
  queryaddr = ipaddr;
  if(ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, &(netif->netmask))) {
    dest = (struct eth_addr *)&ethbroadcast;
  } 
  else if(ip_addr_ismulticast(ipaddr)) {
    /* Hash IP multicast address to MAC address. */
    mcastaddr.addr[0] = 0x01;
    mcastaddr.addr[1] = 0x0;
    mcastaddr.addr[2] = 0x5e;
    mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
    mcastaddr.addr[4] = ip4_addr3(ipaddr);
    mcastaddr.addr[5] = ip4_addr4(ipaddr);
    dest = &mcastaddr;
  } else {

    if(ip_addr_maskcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) {
      /* Use destination IP address if the destination is on the same
         subnet as we are. */
      queryaddr = ipaddr;
    } else {
      /* Otherwise we use the default router as the address to send
         the Ethernet frame to. */
      queryaddr = &(netif->gw);
    }
    dest = arp_lookup(queryaddr);
  }


  /* If the arp_lookup() didn't find an address, we send out an ARP
     query for the IP address. */
  if(dest == NULL) {
    q = arp_query(netif, rtl8019if->ethaddr, queryaddr);
    if(q != NULL) {
	  low_level_send(rtl8019if, q);
      pbuf_free(q);
      return err;
    }
     
    return ERR_MEM;
  }
  ethhdr = p->payload;

  for(i = 0; i < 6; i++) {
    ethhdr->dest.addr[i] = dest->addr[i];
    ethhdr->src.addr[i] = rtl8019if->ethaddr->addr[i];
  }
  
  ethhdr->type = htons(ETHTYPE_IP);
  
  return low_level_send(rtl8019if, p);
	
}


/**
 * Read a packet, clearing overflows.
 */
void 
ne2k_recv_packet(struct netif *netif)
{
  struct RTL8019if *rtl8019if;
  struct eth_hdr *ethhdr;
  struct pbuf *p;

  rtl8019if = netif->state;
  #ifdef LWIP_DEBUG
  PRINT("start get a packet in ne2k_receive_packet\n ");
  #endif
  p = low_level_receive(rtl8019if);
  #ifdef LWIP_DEBUG
  PRINT("got a packet from low_level_receive\n");
  #endif
  
  if(p == NULL) {
    return;
  }
  ethhdr = p->payload;

  switch(htons(ethhdr->type)) {
  case ETHTYPE_IP:
    arp_ip_input(netif, p);
    pbuf_header(p, -14);
    netif->input(p, netif);
    break;
  case ETHTYPE_ARP:
    p = arp_arp_input(netif, rtl8019if->ethaddr, p);
    if(p != NULL) {
	 low_level_send(rtl8019if, p);
	pbuf_free(p);
    }
    break;
  default:
    pbuf_free(p);
    break;
  }
}

